package com.study.web.shiro.filter;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;

import javax.annotation.Resource;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

import org.apache.shiro.session.Session;
import org.apache.shiro.session.mgt.SessionManager;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.AccessControlFilter;
import org.apache.shiro.web.util.WebUtils;
import org.springframework.beans.factory.annotation.Autowired;

import com.study.web.entity.SysUser;
import com.study.web.shiro.session.CustomSessionManager;
import com.study.web.shiro.session.SessionStatus;
import com.study.web.shiro.session.ShiroSessionRepository;
/**
 * redis 会话踢出 过滤器
 * RedisKickoutSessionControlFilter.java
 * @author yaojinshan
 * @since 2018年6月15日上午10:59:29
 */
@SuppressWarnings("all")
public class RedisKickoutSessionControlFilter extends AccessControlFilter {

    private String kickoutUrl; //踢出后到的地址
    private boolean kickoutAfter = false; //踢出之前登录的/之后登录的用户 默认踢出之前登录的用户
    private int maxSession = 1; //同一个帐号最大会话数 默认1
    
    @Resource(name = "redisShiroSessionRepository")
	private ShiroSessionRepository shiroSessionRepository;
    
    @Autowired
	CustomSessionManager customSessionManager;

    private SessionManager sessionManager;

    public void setKickoutUrl(String kickoutUrl) {
        this.kickoutUrl = kickoutUrl;
    }

    public void setKickoutAfter(boolean kickoutAfter) {
        this.kickoutAfter = kickoutAfter;
    }

    public void setMaxSession(int maxSession) {
        this.maxSession = maxSession;
    }

    public void setSessionManager(SessionManager sessionManager) {
        this.sessionManager = sessionManager;
    }

    @Override
    protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception {
        return false;
    }

    @Override
    protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
        Subject subject = getSubject(request, response);
        if(!subject.isAuthenticated() && !subject.isRemembered()) {
            //如果没有登录，直接进行之后的流程
            return true;
        }
        Session session = subject.getSession();
        //标记用户Session
        SessionStatus sessionStatus = (SessionStatus) session.getAttribute(CustomSessionManager.SESSION_STATUS);
        //如果被踢出了，直接退出，重定向到踢出后的地址
//        if (session.getAttribute("kickout") != null) {
    	if (!sessionStatus.getOnlineStatus()) {
            //会话被踢出了
            try {
                subject.logout();
            } catch (Exception e) { //ignore
            }
            saveRequest(request);
            WebUtils.issueRedirect(request, response, kickoutUrl);
            return false;
        }
        
        //获取当前登录人用户名
        String username = ((SysUser) subject.getPrincipal()).getLoginName();
        Serializable sessionId = session.getId();	
        //获取所有用户
        Collection<Session> allSessions = shiroSessionRepository.getAllSessions();
        //同一用户当前在线个数
        List<Session> userSessionList = new ArrayList<Session>();
        SysUser user = null;
        for(Session ss :allSessions){
        	user = customSessionManager.getUserBySession(ss);
        	//用户名相同 并且不是本次请求的session
        	if(user != null && username.equals(user.getLoginName()) && !ss.getId().equals(sessionId) ){
        		userSessionList.add(ss);
        	}
        }
        //如果当前在线人数不为null 并且大于规定的最大在线人数 就随机踢出一个用户
        if(userSessionList != null && userSessionList.size() > (maxSession - 1)){
          try {
        	  Session kickoutSession = userSessionList.get(0);
              //kickoutSession.setAttribute("kickout", true);
              customSessionManager.kickoutUserById(kickoutSession, 0l);
	      } catch (Exception e) {//ignore exception
	    	  e.printStackTrace();
	      }
        }
        return true;
    }
}
